package com.dtp.core.support.runnable;

import org.apache.commons.collections.MapUtils;
import org.slf4j.MDC;

import java.util.Map;

import static com.dtp.common.constant.DynamicTpConst.TRACE_ID;

/**
 * MDC任务处理类
 *
 * MDC（Mapped Diagnostic Contexts）映射诊断上下文，主要用在做日志链路跟踪时，动态配置用户自定义的一些信息，比如：requestId、sessionId等等。
 * MDC使用的容器支持多线程操作，满足线程安全。
 *
 * @author yanhom
 * @since 1.0.8
 *
 * @remark muse
 **/
public class MdcRunnable implements Runnable {
    private final Runnable runnable; // 待执行的任务
    private final Map<String, String> parentMdc; // 为当前线程保存MDC的值

    public MdcRunnable(Runnable runnable) {
        this.runnable = runnable;
        this.parentMdc = MDC.getCopyOfContextMap(); // 获取当前线程的上下文映射副本
    }

    /** 创建MdcRunnable实例对象 */
    public static MdcRunnable get(Runnable runnable) {
        return new MdcRunnable(runnable);
    }

    @Override
    public void run() {
        // 如果没有配置MDC，则直接执行任务即可
        if (MapUtils.isEmpty(parentMdc)) {
            runnable.run();
            return;
        }

        // 否则，将父线程的MDC值分配给子线程
        for (Map.Entry<String, String> entry : parentMdc.entrySet())
            MDC.put(entry.getKey(), entry.getValue());

        try {
            runnable.run(); // 执行任务
        } finally {
            for (Map.Entry<String, String> entry : parentMdc.entrySet())
                if (!TRACE_ID.equals(entry.getKey()))
                    MDC.remove(entry.getKey()); // 只保留"traceId"的MDC，其他全都删除掉
        }
    }
}
